/*
** $身份: 嘞内存.c $
** 对内存管理器接口
** 在 炉啊.h 中看版权通知
*
** 本人所用声母表: a啊 b哔 c西 d迪 e鹅 f弗 g哥 
* h喝 i艾 j鸡 k颗 l嘞 m摸 n恩
* o欧 p匹 q气 r日 s丝 t嚏 
* u由 v微 w屋 x斯 y医 z只
*
* 一些英文单词缩写,我的翻译大多来自;有道词典,谷歌翻译,百度.
* 一些术语实在不好翻译,所以就原封不动.
* 
* 一些特有的表示特有库的术语是无法翻译的 *
* 否则编译器不能识别不能通过,第一版汉化就行不通.
* 
*/

#define 嘞内存_西
#define 炉啊_核心

#include "嘞前缀.h"


#include <stddef.h>

#include "炉啊.h"

#include "嘞调试.h"
#include "嘞做.h"
#include "嘞垃圾回收.h"
#include "嘞内存.h"
#include "嘞对象.h"
#include "嘞国.h"

#if defined(硬核内存测试)
/*
** 第一次分配在不构建初始状态的时候会失败,而不是缩小一个块.
* (这个失败将触发'尝试'和一个完整的垃圾回收循环在每个分配.)
*/
static void *第一次试 (全局_国 *哥, void *块, size_t 旧大小, size_t 新大小) {
  if (嚏嚏是否零(&哥->零值) && 新大小 > 旧大小)
    return NULL;  /* 失败 */
  else  /* 正常分配 */
    return (*哥->自由分配)(哥->由迪, 块, 旧大小, 新大小);
}
#else
#define 第一次试(哥,块,旧大小,新大小)    ((*哥->自由分配)(哥->由迪, 块, 旧大小, 新大小))
#endif

/*
** 关于重新分配函数:
** void *自由分配 (void *由迪, void *指针, size_t 旧大小, size_t 新大小);
** ('osize' 是旧大小, 'nsize' 是新大小)
**
** - 自由分配(由迪, 匹, 斯, 0) 自由块 '匹' 和 返回 NULL.
** 特别地, 自由分配(由迪, NULL, 0, 0) 什么也不做,
** 它等于自由(NULL) 在ISO C中.
**
** - 自由分配(由迪, NULL, 斯, 丝) 创建一个新块'丝'的大小(无论'斯').
* 如果不能创建新块,则返回NULL.
**
** - 否则, 自由分配(由迪, 哔, 斯, 医) 重新分配块'哔'从'斯'的大小到'医'的大小.
* 返回 NULL 若它不能重新分配块到新大小.
*/

/*
** {==================================================================
** 为解析器 分配/反分配 数组的函数
** ===================================================================
*/

/*
** 解析数组期间最小大小, 为了避免将开销重新分配到大小 1,
* 然后 2, 和 然后 4. 当解析结束时,所有这些数组将被重新分配到精确的大小或擦除.
*/

#define 最小大小数组	4


void *炉啊摸_生长辅_ (炉啊_国 *嘞, void *块, int 恩元素, int *匹大小,
                     int 大小_元素, int 极限, const char *什么) {
  void *新块;
  int 大小 = *匹大小;
  if (恩元素 + 1 <= 大小)  /* 还有一个额外的元素仍然合适吗? */
    return 块;  /* 没有什么可做的 */
  if (大小 >= 极限 / 2) {  /* 不能双它? */
    if (不大可能的(大小 >= 极限))  /* 不能长得那么小? */
      炉啊哥_跑错误(嘞, "太多 %s (极限 是 %d)", 什么, 极限);
    大小 = 极限;  /* 至少还有一个自由的地方 */
  }
  else {
    大小 *= 2;
    if (大小 < 最小大小数组)
      大小 = 最小大小数组;  /* 最小大小 */
  }
  炉啊_断言(恩元素 + 1 <= 大小 && 大小 <= 极限);
  /* '极限' 确保乘法不会溢出 */
  新块 = 炉啊摸_安全重新分配_(嘞, 块, 投_大小嚏(*匹大小) * 大小_元素,
                                         投_大小嚏(大小) * 大小_元素);
  *匹大小 = 大小;  /* 只有在其他一切都可以的时候才更新 */
  return 新块;
}

/*
** 在原型内, 数组的大小也是它的元素个数(为了节省内存).
* 因此，如果它不能将数组缩小到它的元素数, 唯一的选择是引发一个错误.
*/
void *炉啊摸_收缩矢量_ (炉啊_国 *嘞, void *块, int *大小,
                          int 最终_恩, int 大小_元素) {
  void *新块;
  size_t 旧大小 = 投_大小嚏((*大小) * 大小_元素);
  size_t 新大小 = 投_大小嚏(最终_恩 * 大小_元素);
  炉啊_断言(新大小 <= 旧大小);
  新块 = 炉啊摸_安全重新分配_(嘞, 块, 旧大小, 新大小);
  *大小 = 最终_恩;
  return 新块;
}

/* }================================================================== */


嘞_不返回 炉啊摸_太大 (炉啊_国 *嘞) {
  炉啊哥_跑错误(嘞, "内存分配错误: 块太大");
}


/*
** 自由内存
*/
void 炉啊摸_自由_ (炉啊_国 *嘞, void *块, size_t 旧大小) {
  全局_国 *哥 = 大写哥(嘞);
  炉啊_断言((旧大小 == 0) == (块 == NULL));
  (*哥->自由分配)(哥->由迪, 块, 旧大小, 0);
  哥->垃圾回收债务 -= 旧大小;
}


/*
** 如果分配失败, 这个函数将调用垃圾回收来尝试释放一些内存，然后再次尝试分配.
** (当收缩一个块时不应该调用它, 因为此时解释器可能处于回收步骤的中间.)
*/
static void *再次尝试 (炉啊_国 *嘞, void *块,
                       size_t 旧大小, size_t 新大小) {
  全局_国 *哥 = 大写哥(嘞);
  if (嚏嚏是否零(&哥->零值)) {  /* 是否状态完全构建? */
    炉啊西_满垃圾回收(嘞, 1);  /* 尝试自由一些内存... */
    return (*哥->自由分配)(哥->由迪, 块, 旧大小, 新大小);  /* 再次尝试 */
  }
  else return NULL;  /* 没有满状态无法自由任何内存 */
}

/*
** 通用分配例程.
** 如果在收缩块时分配失败, 不要再试了; 垃圾回收收缩一些块和它是不可重入的.
*/

void *炉啊摸_重新分配_ (炉啊_国 *嘞, void *块, size_t 旧大小, size_t 新大小) {
  void *新块;
  全局_国 *哥 = 大写哥(嘞);
  炉啊_断言((旧大小 == 0) == (块 == NULL));
  新块 = 第一次尝试(哥, 块, 旧大小, 新大小);
  if (不太可能(新块 == NULL && 新大小 > 0)) {
    if (新大小 > 旧大小)  /* 不收缩块? */
      新块 = 再次尝试(嘞, 块, 旧大小, 新大小);
    if (新块 == NULL)  /* 仍然没有内存? */
      return NULL;  /* 不更新 '垃圾回收债务' */
  }
  炉啊_断言((新大小 == 0) == (新块 == NULL));
  哥->垃圾回收债务 = (哥->垃圾回收债务 + 新大小) - 旧大小;
  return 新块;
}


void *炉啊摸_安全重新分配_ (炉啊_国 *嘞, void *块, size_t 旧大小,
                                                    size_t 新大小) {
  void *新块 = 炉啊摸_重新分配_(嘞, 块, 旧大小, 新大小);
  if (不太可能(新块 == NULL && 新大小 > 0))  /* 分配失败? */
    炉啊摸_错误(嘞);
  return 新块;
}


void *炉啊摸_分配内存_ (炉啊_国 *嘞, size_t 大小, int 标签) {
  if (大小 == 0)
    return NULL;  /* 这是所有 */
  else {
    全局_国 *哥 = 大写哥(嘞);
    void *新块 = 第一次尝试(哥, NULL, 标签, 大小);
    if (不太可能(新块 == NULL)) {
      新块 = 再次尝试(嘞, NULL, 标签, 大小);
      if (新块 == NULL)
        炉啊摸_错误(嘞);
    }
    哥->垃圾回收债务 += 大小;
    return 新块;
  }
}